package com.rizzo.infrastructure.aop;

import com.rizzo.infrastructure.dark.DarkLaunch;
import com.rizzo.infrastructure.dark.DarkLauncher;
import com.rizzo.infrastructure.performance.Monitor;
import com.rizzo.infrastructure.performance.PerformanceMonitor;
import com.rizzo.infrastructure.quota.Quota;
import com.rizzo.infrastructure.quota.QuotaMonitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.core.annotation.Order;

@Aspect
public class ControllerAspect {

	private QuotaMonitor quotaMonitor;

	private PerformanceMonitor performanceMonitor;

	private DarkLauncher darkLauncher;

	public ControllerAspect(QuotaMonitor quotaMonitor, PerformanceMonitor performanceMonitor, DarkLauncher darkLauncher) {
		this.quotaMonitor = quotaMonitor;
		this.performanceMonitor = performanceMonitor;
		this.darkLauncher = darkLauncher;
	}

	@Before("@annotation(darkLaunch)")
	@Order(1)
	public void launchDarkly(JoinPoint joinPoint, DarkLaunch darkLaunch) {
		String darkLaunchName = darkLaunch.name();
		if (this.darkLauncher != null) {
			this.darkLauncher.launchDarkly(darkLaunchName);
		}
	}

	@Before("@annotation(quota)")
	@Order(2)
	public void checkQuota(JoinPoint joinPoint, Quota quota) throws Throwable {
		final long expireSeconds = quota.expireSeconds();
		final long maxCalls = quota.maxCalls();
		final Signature signature = joinPoint.getSignature();
		final String declaringTypeName = signature.getDeclaringTypeName();
		final String method = signature.getName();
		if (this.quotaMonitor != null) {
			this.quotaMonitor.checkQuota(declaringTypeName + "." + method, maxCalls, expireSeconds);
		}
	}

	@Around("@annotation(monitor)")
	@Order(3)
	public Object doPerformanceProfiling(final ProceedingJoinPoint proceedingJoinPoint, Monitor monitor) throws Throwable {
		// start stopwatch
		final Signature signature = proceedingJoinPoint.getSignature();
		final String declaringClass = signature.getDeclaringTypeName();
		final String method = signature.getName();
		final boolean userBound = monitor.userBound();
		if (this.performanceMonitor != null) {
			return this.performanceMonitor.monitorPerformance(declaringClass, method, userBound, new MethodProceeding() {
				@Override
				public Object proceed() throws Throwable {
					return proceedingJoinPoint.proceed();
				}
			});
		} else {
			return proceedingJoinPoint.proceed();
		}
	}

}